package bo.gustavo.scrapper;

import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;

import bo.gustavo.scrapper.model.Persister;
import bo.gustavo.scrapper.model.Url;
import bo.gustavo.scrapper.ui.MainWindow;
import bo.gustavo.scrapper.util.CategoryEnum;
import bo.gustavo.scrapper.util.DataBaseAccessType;
import bo.gustavo.scrapper.util.PropertiesKeys;
import bo.gustavo.scrapper.worker.Parser;
import bo.gustavo.scrapper.worker.ScrapperWorker;

/**
 * Main class that initiates the threads that fetch all the required data for
 * the scrapper.
 * 
 * @author Gustavo Vera
 * */
public class Scrapper {

	private Properties prop;

	private static final String CATEGORY_PATTERN = "%CATEGORY%";

	private boolean stopThread = false;

	private MainWindow mainWindow;

	private int pagesToScrap;

	public Scrapper(MainWindow mainWindow, Properties prop, int pagesToScrap) {
		this.mainWindow = mainWindow;
		this.prop = prop;
		this.pagesToScrap = pagesToScrap;
	}

	/**
	 * Starts the scrapping work using 2 threads.
	 * 
	 * @author Gustavo Vera
	 * */
	public void startSearch() {
		Thread freeAppsThread = new Thread(new PlayScrapperThread(prop.getProperty(PropertiesKeys.BASE_FREE_URL), false));
		Thread paidAppsThread = new Thread(new PlayScrapperThread(prop.getProperty(PropertiesKeys.BASE_PAID_URL), true));
		Thread appScrapper = new Thread(new AppScrapperThread());
		freeAppsThread.start();
		paidAppsThread.start();
		appScrapper.start();
	}

	/**
	 * Method that is invoked to stop the threads and therefore to stop scraping
	 * 
	 * @author Gustavo Vera
	 * */
	public void stopSearch() {
		setStopThread(true);
	}

	public boolean isStopThread() {
		return stopThread;
	}

	public void setStopThread(boolean stopThread) {
		this.stopThread = stopThread;
	}

	public int getPagesToScrap() {
		return pagesToScrap;
	}

	public void setPagesToScrap(int pagesToScrap) {
		this.pagesToScrap = pagesToScrap;
	}

	/**
	 * This class scraps the URLs of apps. The search is performed by
	 * category.There is a base URL where the apps are paginated by CATEGORY. It
	 * implements the Runnable interface because it runs in a thread.
	 * 
	 * @author Gustavo Vera
	 * */
	class PlayScrapperThread implements Runnable {

		private boolean isPaid;

		private String baseUrl;

		PlayScrapperThread(String baseUrl, boolean isPaid) {
			this.baseUrl = baseUrl;
			this.isPaid = isPaid;
		}

		public void run() {
			List<CategoryEnum> categoryList = Arrays.asList(CategoryEnum.values());
			String replacedUrl = "";
			for (CategoryEnum category : categoryList) {
				replacedUrl = StringUtils.replace(baseUrl, Scrapper.CATEGORY_PATTERN, category.name());
				ScrapperWorker worker = new ScrapperWorker(replacedUrl, prop, category, isPaid, pagesToScrap);
				worker.work();

				if (stopThread) {
					break;
				}

			}
		}
	}

	/**
	 * This class scraps the data of all the URL that are present in the table
	 * url and stores the final data to the table app_data. It implements the
	 * Runnable interface so that all the work can be executed by a thread.
	 * 
	 * @author Gustavo Vera
	 * */
	class AppScrapperThread implements Runnable {

		AppScrapperThread() {
		}

		public void run() {
			int totalFetchedApps = 0;
			while (!stopThread) {
				List<Url> urlList = Persister.getInstance().maskOperations(Url.class, null, DataBaseAccessType.QUERY);
				int i = 0;
				StringBuffer buf = new StringBuffer();
				for (Url url : urlList) {
					i++;
					Parser parser = new Parser(url.getAppUrl(), url.getAppTitle(), url.getAppCategory(), url.isPaid());
					parser.beginFetch();
					totalFetchedApps++;
					mainWindow.setNumberAppsScrapper(totalFetchedApps);
					buf.append(url.getId() + ",");
					if (i == 100) {
						String ids = buf.toString();
						Persister.getInstance().maskOperations(null, StringUtils.substringBeforeLast(ids, ","), DataBaseAccessType.DELETE_IN);
						buf = new StringBuffer();
						i = 0;
					}
					if (stopThread) {
						break;
					}
				}
				String ids = buf.toString();
				if (StringUtils.isNotBlank(ids)) {
					Persister.getInstance().maskOperations(null, StringUtils.substringBeforeLast(ids, ","), DataBaseAccessType.DELETE_IN);
				}
				try {
					Thread.currentThread().sleep(10000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		}
	}

}
